//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Windows;
using System.Windows.Markup;
using System.Windows.Media;
using OpenLS.Drawing;
using OpenLS.Drawing.UI;

namespace OpenLS.Spreadsheet.UI.Internals
{
    ///<summary>
    /// Internal cell background control
    ///</summary>
    [Browsable(false)]
    public sealed class CellBackgroundControl : CellControl
    {
        private CellsWindowView _view;

        ///<summary>
        /// Default constructor
        ///</summary>
        public CellBackgroundControl()
        {
            SizeChanged += cellBackgroundControlSizeChanged;
            Unloaded += new RoutedEventHandler(CellBackgroundControlUnloaded);
        }
        internal override void OnWorksheetPaneChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            IWorksheetFragment oldFragment = (IWorksheetFragment)e.OldValue;
            IWorksheetFragment newFragment = (IWorksheetFragment)e.NewValue;
            if (oldFragment != null)
                oldFragment.Worksheet.CellBordersOrBackgroundChanged -= WorksheetBackgroundChanged;
            if (newFragment != null)
                newFragment.Worksheet.CellBordersOrBackgroundChanged += WorksheetBackgroundChanged;

            base.OnWorksheetPaneChanged(d, e);
            DisposeView();
        }

        void WorksheetBackgroundChanged(object sender, System.EventArgs e)
        {
            DisposeView();
            InvalidateSelf(null);
        }

        private void DisposeView()
        {
            if (_view != null)
                _view.Dispose();
            _view = null;
        }

        void CellBackgroundControlUnloaded(object sender, RoutedEventArgs e)
        {
            DisposeView();
        }

        private void cellBackgroundControlSizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (WorksheetPane == null)
                return;
            var size = e.NewSize;
            size = new Size(size.Width, size.Height);
            WorksheetPane.Resize(size);
        }


        protected override void OnRender(DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);
            if (WorksheetPane == null)
                return;
            var columnToCell = new Dictionary<Column, List<Cell>>();
            var rowToCell = new Dictionary<Row, List<Cell>>();
            var cellsToConsider = GetCells();
            cellsToConsider = Enumerable.Where(cellsToConsider,
                                               c =>
                                               c.ActualBackground == null ||
                                               (c.Background is PatternFill && ((PatternFill) c.Background).IsEmpty));
            cellsToConsider = Enumerable.Where(cellsToConsider, c => c.IsVisible);
            foreach (Cell c in cellsToConsider)
            {
               // if (c.ActualBackground == null || (c.Background is PatternFill && ((PatternFill)c.Background).IsEmpty))
                {
                    List<Cell> l;
                    if (!columnToCell.TryGetValue(c.Column, out l))
                    {
                        l = new List<Cell>();
                        columnToCell.Add(c.Column, l);
                    }
                    l.Add(c);
                    if (!rowToCell.TryGetValue(c.Row, out l))
                    {
                        l = new List<Cell>();
                        rowToCell.Add(c.Row, l);
                    }
                    l.Add(c);
                }
            }

            renderRowColumns(drawingContext, columnToCell, rowToCell);
            renderCellBackgrounds(drawingContext);
            renderBarsAndScales(DrawingElement.GetColorScheme(this), drawingContext);
        }
        private static LinearGradientBrush getBarBrush(ColorScheme colorScheme, DataBarInformation dataBarInformation)
        {
            SolidColorBrush leftBrush = ColorToBrushConverter.GetBrush(dataBarInformation.Color, colorScheme);
            Color color1 = leftBrush.Color;
            Color color2 = Color.FromArgb((byte)(color1.A * 0.3), color1.R, color1.G, color1.B);
            var result = new LinearGradientBrush { StartPoint = new Point(0, 0), EndPoint = new Point(1, 0) };
            result.GradientStops.Add(new System.Windows.Media.GradientStop(color1, 0));
            result.GradientStops.Add(new System.Windows.Media.GradientStop(color2, 1));
            return result;
        }
        private void renderCellBackgrounds(DrawingContext drawingContext)
        {
            ColorScheme colorScheme = Worksheet.Workbook.ColorScheme;
            ColorMode mode = SpreadsheetElement.GetColorMode(this);
            var painter = new FillPainter(this, drawingContext, colorScheme, mode);
            foreach (Cell cell in GetRenderedCells())
            {
                if (cell.ActualBackground != null)
                {
                    Rect rect = GetRect(cell);
                    if (rect.Width == 0 || rect.Height == 0)
                        continue;
                    painter.DrawRectangle(cell.ActualBackground, rect);
                }
            }
        }

        private IEnumerable<Cell> GetRenderedCells()
        {
            return GetCells();
        }
        private Rect GetRect(Cell cell)
        {
            return base.GetRelativeRect(cell);
        }

        private void renderBarsAndScales(ColorScheme colorScheme, DrawingContext drawingContext)
        {
            foreach (Cell cell in GetRenderedCells())
            {
                ColorScaleInformation colorScale = cell.ColorScale;
                if (colorScale != null)
                {
                    Rect rect = GetRect(cell);
                    drawingContext.DrawRectangle(new SolidColorBrush(colorScale.GetColor(colorScheme)), null, rect);
                }
                DataBarInformation dataBarInformation = cell.DataBar;
                if (dataBarInformation != null)
                {
                    Rect rect = GetRect(cell);
                    Size size = rect.Size;
                    double barWidth = size.Width;
                    double barHeight = size.Height;
                    barHeight -= 4;
                    if (barHeight < 0)
                        continue;
                    barWidth = barWidth * dataBarInformation.MinRatio +
                               barWidth * dataBarInformation.Value * dataBarInformation.DeltaRatio;
                    Point location = rect.Location;
                    location.Offset(2, 2);
                    rect = new Rect(location, new Size(barWidth, barHeight));
                    LinearGradientBrush brush = getBarBrush(colorScheme, dataBarInformation);
                    drawingContext.DrawRectangle(brush, null, rect);
                }
                IconSetInformation iconSetInformation = cell.Icon;
                if (iconSetInformation != null)
                {
                    Rect rect = GetRect(cell);
                    using (var d = new IconDrawer(rect, drawingContext))
                    {
                        d.Draw(iconSetInformation.Icon);
                    }
                }
            }
        }


        private double GetX(Column column)
        {
            return GetX(column.Index);

        }
        private double GetRightX(Cell cell)
        {
            return GetX(cell.Column.Index + cell.ColumnSpan);

        }

        internal double GetX(int columnIndex)
        {
            return Worksheet.Columns.GetOffset(columnIndex) - Worksheet.Columns.GetOffset(WorksheetPane.Left);

        }

        internal double GetY(Row row)
        {
            return GetY(row.Index);
        }
        internal double GetBottomY(Cell cell)
        {
            return GetY(cell.Row.Index + cell.RowSpan);
        }
        internal double GetBottomY(Row row)
        {
            return GetY(row.Index + 1);
        }

        internal double GetY(int rowIndex)
        {
            return Worksheet.Rows.GetOffset(rowIndex) - Worksheet.Rows.GetOffset(WorksheetPane.Top);

        }
        private void renderRowColumns(DrawingContext drawingContext, Dictionary<Column, List<Cell>> columnToCells, Dictionary<Row, List<Cell>> rowToCells)
        {
            ColorMode mode = SpreadsheetElement.GetColorMode(this);
            if (mode == ColorMode.BlackAndWhite)
                return;
            FillPainter painter = new FillPainter(this, drawingContext, WorksheetPane.Worksheet.Workbook.ColorScheme, mode);
            List<Row> nonEmptyRows =
                Enumerable.ToList(Enumerable.Where(WorksheetPane.GetRows(), r => r.Background != null && r.IsVisible));
            foreach (Column column in Enumerable.Where(WorksheetPane.GetColumns(), col => col.Background != null))
            {
                if (column.Background == null)
                    continue;
                List<Cell> allCells_;
                if (!columnToCells.TryGetValue(column, out allCells_) && nonEmptyRows.Count == 0)
                {
                    painter.DrawRectangle(column.Background,
                                          new Rect(
                                              new Point(Worksheet.Columns.GetOffset(column.Index) -
                                                  Worksheet.Columns.GetOffset(WorksheetPane.Left),
                                                  0),
                                              new Point(
                                                  Worksheet.Columns.GetOffset(column.Index + 1) -
                                                  Worksheet.Columns.GetOffset(WorksheetPane.Left),
                                                  ActualHeight)));
                }
                else
                {
                    Contract.Assert(allCells_ != null || nonEmptyRows.Count > 0);
                    IEnumerable<Row> cellRows = allCells_ != null ? Enumerable.SelectMany(allCells_, c => Enumerable.Where(c.GetRows(), r => r.IsVisible)) : Enumerable.Empty<Row>();
                    var allRows = Enumerable.ToList(Enumerable.Distinct(Enumerable.Union(cellRows, nonEmptyRows)));

                    allRows.Sort((c1, c2) => c1.Index.CompareTo(c2.Index));
                    painter.DrawRectangle(column.Background, new Rect(  new Point(GetX(column), 0), new Point(GetX(column.Index + 1),GetY(allRows[0]))));
                    for (int i = 0; i < allRows.Count - 1; i++ )
                    {
                        painter.DrawRectangle(column.Background, new Rect(new Point(GetX(column), GetBottomY(allRows[i])), new Point(GetX(column.Index + 1), GetY(allRows[i + 1]))));

                    }
                    painter.DrawRectangle(column.Background, new Rect(new Point(GetX(column), GetBottomY(allRows[allRows.Count-1])), new Point(GetX(column.Index + 1), ActualHeight)));

                }

            }
            
            foreach (Row row in Enumerable.Where(WorksheetPane.GetRows(), r => r.Background != null))
            {
                if (row.Background == null)
                    continue;
                List<Cell> allCells;
                if (!rowToCells.TryGetValue(row, out allCells))
                {
                    painter.DrawRectangle(
                        row.Background,
                        new Rect(new Point(
                                     0,
                                     Worksheet.Rows.GetOffset(row.Index) -
                                     Worksheet.Rows.GetOffset(WorksheetPane.Top)
                                     ),
                                 new Point(ActualWidth,
                                           Worksheet.Rows.GetOffset(row.Index + 1) -
                                           Worksheet.Rows.GetOffset(WorksheetPane.Top)
                                     )));
                }
                else
                {
                    allCells.Sort((c1, c2) => c1.Column.Index.CompareTo(c2.Column.Index));
                    painter.DrawRectangle(row.Background,
                                          new Rect(new Point(0, GetY(row)),
                                                   new Point(GetX(allCells[0].Column), GetY(row.Index + 1))));
                    for (int i = 0; i < allCells.Count - 1; i++)
                    {
                        painter.DrawRectangle(row.Background,
                                              new Rect(new Point(GetRightX(allCells[i]), GetY(row)),
                                                       new Point(GetX(allCells[i + 1].Column), GetY(row.Index + 1))));
                    }
                    painter.DrawRectangle(row.Background,
                                          new Rect(new Point(GetRightX(allCells[allCells.Count - 1]), GetY(row)),
                                                   new Point(ActualWidth, GetY(row.Index + 1))));
                }
            }
        }

        protected override IEnumerable<Cell> GetCells()
        {
            if (WorksheetPane is WorksheetPane)
                return this.View.GetCells();
            return base.GetCells();
        }
        
        private CellsWindowView View
        {
            get
            {
#if DEBUG
                Contract.Assert(_view == null || !_view._disposed   );
#endif
                if (_view == null)
                    _view = new CellsWindowView(((WorksheetPane) this.WorksheetPane).Window,
                                                c =>
                                                c.ActualBackground != null || c.DataBar != null || c.Icon != null ||
                                                c.ColorScale != null
                                               );
                return _view;
            }
        }


        protected override bool IsRenderProperty(string name)
        {
            switch (name)
            {
                case CellProperties.Background:
                case CellProperties.ActualBackground:
                case CellProperties.Width:
                case CellProperties.ActualWidth:
                case CellProperties.Height:
                case CellProperties.ActualHeight:
                case CellProperties.IsVisible:
                // case CellProperties.IsHidden:
                case CellProperties.DifferentialStyleRecord:
                case CellProperties.DataBar:
                case CellProperties.ColorScale:

                    return true;
                case CellProperties.ActualFontWeight:
                case CellProperties.ActualFontStyle:
                case CellProperties.ActualFontUnderline:
                case CellProperties.ActualBorders:
                case CellProperties.Borders:
                case CellProperties.FontWeight:
                case CellProperties.FontStyle:
                case CellProperties.FormattedValue:
                case CellProperties.FormulaFormattedValue:
                case CellProperties.Value:
                case CellProperties.IsSelected:
                case CellProperties.FontUnderline:
                case CellProperties.FontFamily:
                case CellProperties.ActualFontSize:
                case CellProperties.FontSize:
                case CellProperties.Foreground:
                case CellProperties.ActualForeground:
                case CellProperties.Format:
                case CellProperties.VerticalAlignment:
                case CellProperties.HorizontalAlignment:
                case CellProperties.TextAlignment:
                case CellProperties.TextWrapping:
                case CellProperties.Indentation:
                case CellProperties.CanOverflow:
                case CellProperties.LocalizedFormula:
                case CellProperties.Formula:
                case CellProperties.Row:
                case CellProperties.Column:
                case CellProperties.Index:
                case CellProperties.IsLocked:
                case CellProperties.ArrayFormulaRange:
                case CellProperties.Comment:
                case CellProperties.Offset:
                    return false;
            }
            Debug.WriteLine(string.Format("unhandled property {0} in {1}", name, GetType()));
            return false;
        }
    }
}